package com.greyshark.lib.bluetooth.scan;

import android.Manifest;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.greyshark.lib.bluetooth.scan.scanner.BluetoothLeScannerCompat;
import com.greyshark.lib.bluetooth.scan.scanner.ScanCallback;
import com.greyshark.lib.bluetooth.scan.scanner.ScanFilter;
import com.greyshark.lib.bluetooth.scan.scanner.ScanResult;
import com.greyshark.lib.bluetooth.scan.scanner.ScanSettings;
import com.greyshark.lib.bluetooth.utils.BleLogUtils;

import java.util.ArrayList;
import java.util.List;

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class NordicsemiBleScanner extends BaseBleScanner {
    private final static String TAG = NordicsemiBleScanner.class.getSimpleName();

    private Handler stopHandler = null;

    private BluetoothAdapter mBluetoothAdapter = null;
    private SimpleScanCallback mScanCallback = null;

    Context mContext;

    public NordicsemiBleScanner(Context context, SimpleScanCallback callback) {
        this.mContext = context;
        mScanCallback = callback;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        stopHandler = new Handler(Looper.getMainLooper());
    }

    @Override
    public void onStartBleScan() {
        synchronized (NordicsemiBleScanner.this) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                    BleLogUtils.e(TAG, "Failed! Missing permissions:" + Manifest.permission.BLUETOOTH_SCAN);
                    return;
                }
            }else /*if(Build.VERSION.SDK_INT < Build.VERSION_CODES.S)*/{
                if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    BleLogUtils.e(TAG, "Failed! Missing permissions:" + Manifest.permission.ACCESS_COARSE_LOCATION);
                    return;
                }
            }
            isStop = false;
            if (stopHandler != null) stopHandler.removeCallbacksAndMessages(null);
            BleLogUtils.d(TAG, "onStartBleScan() isScanning:" + isScanning);
            if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() && !isScanning) {
                try {
                    final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
                    final ScanSettings settings = new ScanSettings.Builder()
                            .setLegacy(false)
                            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                            .setReportDelay(1000)
                            .setUseHardwareBatchingIfSupported(false)
                            .build();
                    final List<ScanFilter> filters = new ArrayList<>();
                    filters.add(new ScanFilter.Builder().setServiceUuid(null).build());
                    scanner.startScan(filters, settings, scanCallback);
                    isScanning = true;
                    BleLogUtils.d(TAG, "StartBleScan");
                } catch (Exception e) {
                    isScanning = false;
                    if (mScanCallback != null) {
                        mScanCallback.onBleScanStop(MyBleScanState.BLUETOOTH_OFF);
                    }
                    BleLogUtils.e(TAG, e.toString());
                }
            } else {
                if (mScanCallback != null) {
                    mScanCallback.onBleScanStop(MyBleScanState.BLUETOOTH_OFF);
                }
            }
        }
    }

    @Override
    public void onStopBleScan() {
        synchronized (NordicsemiBleScanner.this) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                    BleLogUtils.e(TAG, "Failed! Missing permissions:" + Manifest.permission.BLUETOOTH_SCAN);
                    return;
                }
            }else /*if(Build.VERSION.SDK_INT < Build.VERSION_CODES.S)*/{
                if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    BleLogUtils.e(TAG, "Failed! Missing permissions:" + Manifest.permission.ACCESS_COARSE_LOCATION);
                    return;
                }
            }
            BleLogUtils.d(TAG, "onStopBleScan()");
            isStop = true;
            if (stopHandler != null) {
                stopHandler.removeCallbacksAndMessages(null);
                stopScanRunnable = new StopScanRunnable();
                stopHandler.postDelayed(stopScanRunnable, STOP_SCAN_TIME_DELAY);
            }
        }
    }

    private StopScanRunnable stopScanRunnable;

    private class StopScanRunnable implements Runnable {
        @Override
        public void run() {
            try {
                BluetoothLeScannerCompat.getScanner().stopScan(scanCallback);
                isScanning = false;
                BleLogUtils.d(TAG, "stopBleScan");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public void onBleScanFailed(MyBleScanState scanState) {
        if (mScanCallback != null) {
            mScanCallback.onBleScanStop(scanState);
        }
    }

    private final ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(final int callbackType, @NonNull final ScanResult result) {
            // do nothing
        }

        @Override
        public void onBatchScanResults(@NonNull final List<ScanResult> results) {
            if (mScanCallback != null && !isStop) {
                mScanCallback.onBleScan(results);
            }
        }

        @Override
        public void onScanFailed(final int errorCode) {
            // should never be called
        }
    };
}
